﻿using Reticulum.User;
using Reticulum.工具类;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Reticulum
{
    class Upload
    {
        //FileInfo类说明
        #region
        /* FileInfo 类    命名空间:System.IO    相关网站：https://docs.microsoft.com/zh-cn/dotnet/api/system.io.fileinfo?redirectedfrom=MSDN&view=netcore-3.1
         * 
         * 说明：提供用于创建、复制、删除、移动和打开文件的属性和实例方法，并且帮助创建 FileStream 对象。 无法继承此类。
         * 
         * 属性：✦ Directory       -  属性检索一个对象，该对象表示文件的父目录。
         *       ✦ DirectoryName   -  属性检索文件的父目录的完整路径。
         *       ✦ Exists          -  属性检查是否存在文件，然后再对其进行操作。
         *       ✦ IsReadOnly      -  属性检索或设置一个值，该值指定是否可以修改文件。
         *       ✦ Length          -  检索文件的大小。
         *       ✦ Name            -  检索文件的名称。
         *       
         * 枚举：★ FileAccess  -  指定对文件的读取和写入访问权限。
         *       ★ FileShare   -  指定已在使用的文件所允许的访问级别。
         *       ★ FileMode    -  指定是保留还是覆盖现有文件的内容，以及创建现有文件的请求是否会引发异常。
         * **/
        #endregion



        public static void StartUpLoad()
        {
            if (ThisMyInfo.UploadState == "1")
            {
                MessageBox.Show("你被禁止进行上传!");

            }
            else
            {
                OpenFileDialog openPic = new OpenFileDialog();// 实例化OpenFileDialog控件  (打开文件对话框)
                openPic.Filter = "所有文件 =￣ω￣=|*.*";     // Filter： 获取或设置当前文件名筛选器字符串，例如，“文本文件(.txt) |.txt | 所有文件(.) ||.”
                openPic.FilterIndex = 1;// FilterIndex 在对话框中选择的文件筛选器的索引，如果选第一项就设为1
                openPic.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);// InitialDirectory：对话框的初始目录 - 获取桌面文件夹路径为初始地址


                //判断我当前是否上传文件                                                                                    
                if (openPic.ShowDialog() == DialogResult.OK)
                {
                    //string strPath = openPic.FileName;//例如：C:\Users\Overlord\Desktop\图片.jpg
                    string strName = openPic.FileName.Substring(openPic.FileName.LastIndexOf("\\") + 1);//获取文件名例如：图片.jpg
                    string strSuffix = strName.Substring(strName.LastIndexOf("."));//获取后缀名例如：.jpg
                    string[] strSavename = strName.Split('.');//即可得到sArray[0]="图片",sArray[1]="jpg";
                    bool Whethertoexecute = false;//True则判断此文件存在，False则为文件不存在

                    SqlDataReader reader = DBHelps.Select(string.Format("select * from FlieInfo where UserID={0} and original_File_name = '{1}'", ThisMyInfo.UserID, (strSavename[0] + strSuffix)));
                    if (reader.Read())
                        Whethertoexecute = true;

                    reader.Close();
                    DBHelps.reader = null;

                    if (Whethertoexecute)
                    {
                        DialogResult result = MessageBox.Show("已有此文件，是否继续上传？", "", MessageBoxButtons.YesNo);
                        if (result == DialogResult.Yes)
                            strName = TraverseRename(strSavename[0], strSuffix, 0);
                        else
                            return;
                    }
                    
                    FileInfo fileInfo = new FileInfo(openPic.FileName);//实例化FileInfo类，并获取用户选择的文件

                    Client.FlieLLength = (int)fileInfo.Length;//获取文件大小 字节单位

                    if (fileInfo.Length > 5242880)//判断文件大小不能超过20K，fileInfo.Length是以字节（B）为单位的,//1024B =1KB  1024K=1M
                    {
                        MessageBox.Show("上传的文件不能大于5Mb");
                    }
                    else
                    {
                        
                        //return;

                        //文件合法，执行上传！
                        string sqlstr1 = string.Format("insert into FlieTeamLog(FlieID,UserID,FileDownName,SaveName,OperationState,Createdate,FileSize,FileSuffix)" +
                            "values({0}, {1}, '{2}', '{3}', '{4}', '{5}',{6},'{7}')", 0, ThisMyInfo.UserID, null, strName, null, DateTime.Now, fileInfo.Length, strSuffix);
                        //第一个null为，文件ID，上传后更新，
                        //第二个null为，下载用的文件名，上传后更新，
                        //第三个null为，队列状态，创建任务时更新

                        if (DBHelps.Command(sqlstr1))
                        {   //判断加入的任务队列,True为等待队列、False为下载队列
                            bool IfWait = true;

                            for (int i = 0; i < UploadList.Length; i++)
                            {
                                if (UploadList[i] == null)//如果上传队列为空则进行上传
                                {   //加入的任务队列为上传队列
                                    IfWait = false;

                                    //更新下载日志为正在上传
                                    DBHelps.Command(string.Format("update FlieTeamLog set OperationState='正在上传' where SaveName='{0}' and DownloadLocation is null", strName));

                                    //赋值匿名方法 到 Action委托数组，并开启新线程进行上传
                                    UploadList[i] = UploadCMD;//delegate

                                    //开始执行异步委托,进行上传
                                    UploadList[i].BeginInvoke(i, openPic.FileName, strName, null, null);
                                    break;
                                }
                            }
                            if (IfWait)//上述循环结束，判断是否加入等待队列
                            {
                                //如果队列中全都在进行下载则进入等待队列
                                DBHelps.Command(string.Format("update FlieTeamLog set OperationState='等待上传' where SaveName='{0}' and DownloadLocation is null", strName));
                                for (int i = 0; i < WaitloadList.Length; i++)
                                {
                                    if (WaitloadList[i] != null)
                                    { WaitloadList[i] = UploadCMD; }
                                    else 
                                    {
                                        MessageBox.Show("队列已饱和，请闲置后上传！");
                                        break;
                                    }
                                }
                                LoadListView.CountQueue();//刷新队列
                            }
                        }
                        else
                        {
                            MessageBox.Show("创建任务失败！");
                        }
                    }
                }
            }
        }



        public static void UploadCMD(int overindex, string openPic, string SaveName)//异步委托的方法
        {
            Client.strPicPath = openPic;  //对文件名（strPicPath）称进行赋值

            //button1.BackgroundImage = Image.FromFile(strPicPath);  //将选择的文件展示给用户

            Client.extension = Path.GetExtension(Client.strPicPath);  // 单独获取文件后缀名 (带点)

            Client.saveName = Path.GetFileNameWithoutExtension(Client.strPicPath);  // 单独获取文件名

            Client.original_file_name = Client.saveName + Client.extension;//获取原文件名

            string a = "http://112.74.46.59/Upload.ashx";//本地服务器地址
                                                         //string a = "https://localhost:44323/Upload.ashx";//本地服务器地址
            string b = Client.strPicPath;//上传文件的全路径

            string c = (LoginHome.account + "_" + Client.saveName + new Random().Next(0, 9999) + Client.extension).ToString();
            //保存后的文件名名：用户账号 +_+ 文件名+ 随机数 + 文件后缀

            Client.upload_file_name = c;//获取上传文件名
            Upload.HttpUploadFile(overindex, SaveName, a, b, c);
        }




        public static string HttpUploadFile(int overindex,string Fiename,string address, string fileNamePath, string downloadname = "")//上传的方法
        {
            int returnValue = 0;

            int pos = fileNamePath.LastIndexOf("\\");
            if (downloadname == "")
                downloadname = fileNamePath.Substring(pos + 1);

            FileStream fs = new FileStream(fileNamePath, FileMode.Open, FileAccess.Read);
            BinaryReader r = new BinaryReader(fs);     //时间戳   
            string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + strBoundary + "\r\n");     //请求头部信息   
            StringBuilder sb = new StringBuilder();
            sb.Append("--");
            sb.Append(strBoundary);
            sb.Append("\r\n");
            sb.Append("Content-Disposition: form-data; name=\"");
            sb.Append("file");
            sb.Append("\"; filename=\"");
            sb.Append(downloadname);
            sb.Append("\";");
            sb.Append("\r\n");
            sb.Append("Content-Type: ");
            sb.Append("application/octet-stream");
            sb.Append("\r\n");
            sb.Append("\r\n");
            string strPostHeader = sb.ToString();
            byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);     // 根据uri创建HttpWebRequest对象   
            HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(address));
            httpReq.Method = "POST";     //对发送的数据不使用缓存   
            httpReq.AllowWriteStreamBuffering = false;     //设置获得响应的超时时间（300秒）   
            httpReq.Timeout = 300000;
            httpReq.ContentType = "multipart/form-data; boundary=" + strBoundary;
            long length = fs.Length + postHeaderBytes.Length + boundaryBytes.Length;
            long fileLength = fs.Length;
            httpReq.ContentLength = length;
            string sReturnString = "";
            try
            {
                //每次上传10mb 
                int bufferLength = 5242880;
                byte[] buffer = new byte[bufferLength];
                int size = r.Read(buffer, 0, bufferLength);
                Stream postStream = httpReq.GetRequestStream();         //发送请求头部消息   
                postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
                while (size > 0)
                {
                    postStream.Write(buffer, 0, size);
                    //Application.DoEvents();
                    size = r.Read(buffer, 0, bufferLength);
                }

                //添加尾部的时间戳   
                postStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                postStream.Close();         //获取服务器端的响应   
                WebResponse webRespon = httpReq.GetResponse();
                Stream s = webRespon.GetResponseStream();
                //读取服务器端返回的消息  
                StreamReader sr = new StreamReader(s);
                sReturnString = sr.ReadLine();
                s.Close();
                sr.Close();

                string[] other = Fiename.Split('.');
                //将文件信息存入数据库
                string DML2 = string.Format("insert into FlieInfo(FlieName, FlieLength, FlieRoute, FlieSuffix, FlieGetTime, UserID,original_File_name,FileDownName)" +
                    "values('{0}',{1},'{2}','{3}','{4}','{5}','{6}','{7}')", other[0], Client.FlieLLength, "没发布，所有没有路径 :)", Client.extension, DateTime.Now, Reticulum.User.ThisMyInfo.UserID, Client.original_file_name, Client.upload_file_name);
                bool Results = DBHelps.Command(DML2);
                if (Results)
                {
                    string fileid = "";

                    string DML3 = string.Format("select FlieID from FlieInfo");
                    SqlDataReader reader = DBHelps.Select(DML3);
                    if (reader.Read())
                        fileid = reader["FlieID"].ToString();

                    reader.Close();
                    DBHelps.reader = null;



                    //上传结束：更新下载日志为完成上传
                    DBHelps.Command(string.Format("update FlieTeamLog set OperationState='上传完成',FlieID ={0},FileDownName='{1}' where SaveName='{2}' and DownloadLocation is null", fileid, downloadname, Fiename));

                    //清空上传队列
                    UploadList[overindex] = null;
                    MessageBox.Show("文件上传成功！", "d===(￣▽￣*)b", MessageBoxButtons.OK);
                }
                else
                {
                    MessageBox.Show("文件上传失败！", "警告", MessageBoxButtons.OK);
                }
              
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.Message);
                DBHelps.reader = null;

                //重新执行
                HttpUploadFile(overindex, Fiename, address, fileNamePath, downloadname = "");

            }
            return sReturnString;
        }





        public static string TraverseRename(string Filename,string Suffix,int overindex)//重新绘制名字
        {
            overindex++;
            bool Continue = false;
            //如果查询到的文件名存在
            SqlDataReader reader = DBHelps.Select(string.Format("select * from FlieInfo where UserID={0} and original_File_name = '{1}'", ThisMyInfo.UserID, (Filename+Suffix)));
            if (reader.Read())
                Continue = true;
            reader.Close();
            DBHelps.reader = null;



            //为True则遍历
            //为Flass则返回结果
            if (Continue)
            {
                string[] strSavename = Filename.Split('(');
                Filename = strSavename[0] + "(" + overindex + ")";
                return TraverseRename(Filename, Suffix, overindex);
            }
            else
                return Filename + Suffix;

            return Filename;
        }





















        //上传队列
        public static Action<int, string, string> action4;
        public static Action<int, string, string> action5;
        public static Action<int, string, string> action6;

        //等待队列
        public static Action<int, string, string> wait1;
        public static Action<int, string, string> wait2;
        public static Action<int, string, string> wait3;
        public static Action<int, string, string> wait4;
        public static Action<int, string, string> wait5;
        public static Action<int, string, string> wait6;
        public static Action<int, string, string> wait7;

        public static Action<int, string, string>[] UploadList = new Action<int, string, string>[] { action4, action5, action6 };
        public static Action<int, string, string>[] WaitloadList = new Action<int, string, string>[] { wait1, wait2, wait3, wait4, wait5, wait6, wait7 };
    }
}
